import Geo from './Geo.js';
import Mat from './Mat.js';
import Scene from './Scene';
import { CreateProgram } from '../../component/Utils';
import GameApplication from '../../system/GameApplication';
import GameObject from './GameObject';
import Mesh from '../Mesh/Mesh';
import MeshLD from '../Mesh/MeshLD';


export default class Frame extends Scene {
    public static frame: Frame;

    //分辨率
    public height: number = 1024;
    public width: number = 1024;

    public texture: any;//当前渲染贴图
    public textureLast: any;//二次渲染图
    public textureDepth: any;
    public frameBuffer: any;
    public depthBuffer: any;

    public drawObjsLast: Map<string, Mesh[]>;

    public constructor(name: string, gl: any) {
        super(name, gl);
        Frame.frame = this;
        this.drawObjsLast = new Map<string, Mesh[]>;
    }

    public init() {
        const { gl } = this;

        this.texture = gl.createTexture();
        this.textureLast = gl.createTexture();
        this.textureDepth = gl.createTexture();
        this.frameBuffer = gl.createFramebuffer();
        this.depthBuffer = gl.createRenderbuffer();
    }

    public update(time: number) {
        super.update(time);

        const pvM4 = GameApplication.getCamera()!.getPvM4();
        this.setUniform('u_PvM4', { value: pvM4.elements });
    }

    public updateDraw(_texture: any) {
        const { gl, width, height } = this;
        const textureIndex = 0;
        this._createTextureBuffer(textureIndex, _texture);
        this._createFrameBuffer(textureIndex, _texture);
        this._createDepthBuffer();
        //设置渲染尺寸
        gl.viewport(0, 0, width, height);
    }

    updateOfView() {
        const { gl, width, height } = this;
        //设置渲染尺寸
        gl.viewport(0, 0, width, height);
    }

    public reset() {
        const { gl } = this;
        const { canvas: { width, height } } = gl;
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        gl.bindRenderbuffer(gl.RENDERBUFFER, null);
        gl.bindTexture(gl.TEXTURE_2D, null);
        gl.viewport(0, 0, width, height);
    }

    //#region draw
    public draw([r, g, b, a] = [0, 0, 0, 1]) {
        this.updateDraw(this.texture);
        super.draw([r, g, b, a]);
        this.reset();

        return this.texture;
    }

    //二次渲染
    public drawLast([r, g, b, a] = [0, 0, 0, 1]) {
        const { drawObjsLast, programs, gl } = this;
        this.updateDraw(this.textureLast);
        super.draw([r, g, b, a]);
        for (let [key, value] of drawObjsLast) {
            const { program, attribs, uniforms } = programs.get(key) as any;
            gl.useProgram(program);
            value.forEach(element => {
                const mesh = element as unknown as Mesh;
                mesh.updateData(gl, attribs, uniforms);
                mesh.draw(gl);
            });
        }
        this.reset();
        return this.textureLast;
    }

    //渲染到视图上
    drawView([r, g, b, a] = [0, 0, 0, 1]) {
        this.updateOfView();
        super.draw([r, g, b, a]);
    }
    //#endregion

    //#region 帧缓冲区
    //创建纹理贴图 Texture
    _createTextureBuffer(index: number, _texture: any) {
        const { gl, width, height } = this;

        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
        gl.activeTexture(gl[`TEXTURE${index}`]);

        gl.bindTexture(gl.TEXTURE_2D, _texture);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);//设置滤波器
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    }
    //创建帧缓冲区 FrameBuffer
    _createFrameBuffer(index: number, _texture: any) {
        const { gl, frameBuffer } = this;

        gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, _texture, index);
    }
    //创建深度缓冲区 depthBuffer
    _createDepthBuffer() {
        const { gl, width, height, depthBuffer } = this;
        gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
        gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);//配装深度缓冲区
        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);//向缓冲区中添加渲染缓冲区
    }
    //#endregion


    public add(...child: GameObject[]) {
        const { gl, drawObjsLast, drawObjs } = this;

        super.add(...child);
        child.forEach(u => {

            //递归出所有子类
            let child = this._getChildren(u);
            child.push(u);
            for (let obj of child) {
                u.init(gl);
                const mesh = obj as unknown as Mesh;
                if (mesh instanceof Mesh) {
                    if (mesh.isLD) {
                        this.addDrawObjs(mesh, drawObjsLast);
                    } else {
                        this.addDrawObjs(mesh, drawObjs);
                    }
                }
            }
        });
    }

    public remove(element: GameObject) {
        const { drawObjsLast, drawObjs } = this;
        if (super.remove(element)) {

            let child = this._getChildren(element);
            child.push(element);
            for (let obj of child) {
                const mesh = obj as unknown as Mesh;
                if (mesh instanceof Mesh) {
                    if (mesh.isLD) {
                        this.addDrawObjs(mesh, drawObjsLast);
                    } else {
                        this.addDrawObjs(mesh, drawObjs);
                    }
                }
            }
            return true;
        }
        return false;
    }

    public setTexture() {
        const { drawObjsLast, texture } = this;

        for (let [key, value] of drawObjsLast) {

            value.forEach(element => {
                const meshld = element as unknown as MeshLD;
                meshld.setTextureDraw(texture);
            });
        }
    }

}